# !/usr/bin/env python3
# -*- coding:utf-8 -*-
# Auther : Liang_lz
# Date : 2023/7/27

import requests
from datetime import datetime
from util import StringUtil
from bs4 import BeautifulSoup


class Ticket:
    """
    票据
    """
    name = None  # Web的大标签
    _type = None  # 话剧类型
    image_url = None  # 图片URL
    status = None  # 是否开售
    address = None  # 哪里观看
    open_time = None  # 开售时间
    price = None  # 价格

    # 定义构造器方法
    def __init__(self, name: str, _type: str, images_url: str, status: str, address: str, open_time: str, price: int):
        # 方法级别注释：
        """
        创建一个票据
        :param name: 名称
        :param _type: 类型
        :param images_url: 封面
        :param status: 状态
        :param address: 演出地址
        :param open_time: 开售时间
        :param price: 价格(取整)
        """
        self.name = name
        self._type = _type
        self.image_url = images_url
        self.status = status
        self.address = address
        self.open_time = open_time
        self.price = price

        pass

# 定义个运行器类
class Runner:
    """
    运行器
    """
    # 定义错误日志
    __inner_log_tpl = "{} [{}] {}"  # 占位符号: 日志的类型、当前时间、文本内容

    # 模拟web请求
    __default_headers = {
        # 下述双引号，若全部都是双引号则使用单引号括起来，反之；都有则用三引号
        "Sec-Ch-Ua": 'Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115',
        # 我的系统
        "Sec-Ch-Ua-Platform": "Windows",
        # ua标识
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183"
    }

    def run(self, _p: int):
        """
        核心运行方法
        :return: None
        """

        if _p is None:
            return

        if _p <= 0:
            return

        try:
            items = self.parse_page_text_to_list(self.fetch_page_by_id("https://www.moretickets.com/list/1101-all/latest", _id=_p))
            print("解析的条目：", len(items))
        except requests.exceptions.ConnectionError as e:
            self.__print_error("发生错误：{}".format(e))

    # 抓取分页
    def fetch_page_by_id(self, url: str, _id: int = 0, headers=None) -> None | str:
        """
        通过ID找到页面，然后下载
        :param url: URL
        :param _id: 页面ID
        :param headers: 自定义的头部信息
        :return: 页面的文本表示
        """
        if headers is None:
            headers = {}
        self.__print_info("准备获取页面 {} 的内容....".format(_id))

        # Todo 处理多个不同 _id 的页面,分页功能的实现

        url_trimmed = StringUtil.clean_str(url)
        if url_trimmed is None:
            self.__print_error("指定的目标地址为空： {}".format(url_trimmed))
            return None

        # 键值对长度 <= 0直接发请求
        if _id != 0:
            url_trimmed = url_trimmed + "/p{}".format(_id)

        if headers is None:
            result = requests.get(url=url_trimmed, headers=self.__default_headers)  # requests.get获取网页信息
        else:
            current_headers = self.__default_headers.copy()  # 深拷贝
            current_headers.update(headers)  # 用字典更新字典
            result = requests.get(url=url_trimmed, headers=self.__default_headers)
        if result is None:
            print(self.__print_warn("结果未响应"))
            return None

        return result.text

    def parse_page_text_to_list(self, text: str):
        """
        解析指定的页面信息
        :param text: 页面内容(HTML格式)
        :return: 数据列表
        """
        text_trimmed = StringUtil.clean_str(text)
        if text_trimmed is None:
            return None

        curr_res_obj = BeautifulSoup(text_trimmed, features='lxml')
        # div标签名称 属性class里有shows-container  名称的
        curr_show_container = curr_res_obj.find(name="div", attrs={"class": "shows-container"})
        # 查找子节点a标签的，要求为class类对应show-items sa_entrance 并且 data-sasectionname 对应 演出列表
        all_show_items = curr_show_container.find_all("a", attrs={"class": "show-items sa_entrance", "data-sasectionname": "演出列表"})

        res = []

        for curr_show_items in all_show_items:
            # 查找叶子节点标签为：data-sashowname的数据  (话剧名称)
            curr_res_show_time_name = curr_show_items.get("data-sashowname")
            if curr_res_show_time_name is None:
                continue

            # 查找叶子节点标签为：data-showtypedisplayname的数据  (话剧类型)
            curr_res_show_time_type = curr_show_items.get("data-showtypedisplayname")
            if curr_res_show_time_type is None:
                continue

            # 查找图片
            curr_img = curr_show_items.find(name="img")
            if curr_img is None:
                continue

            # 上行的代码输出得出 赋值到data-src中
            curr_res_image_url = curr_img.get("data-src")
            if curr_res_image_url is None:
                continue

            # 查询是否开售
            curr_tags = curr_show_items.find(name="div", attrs={"class": "tags"})
            if curr_tags is None:
                continue

            curr_tag = curr_tags.find(name="span", attrs={"class": "tag"})
            if curr_tag is None:
                continue

            curr_res_status = curr_tag.text
            # 获取地址
            curr_res_address = curr_show_items.find(name="div", attrs={"class": "show-addr"})
            if curr_res_address is None:
                continue
            curr_res_address = curr_res_address.text

            # 获取多少钱
            curr_res_price = curr_show_items.find(name="div", attrs={"class": "show-price"})
            if curr_res_price is None:
                continue
            curr_res_price = curr_res_price.text

            # 获取开始时间&&处理时间
            curr_res_open_time = curr_show_items.find(name="div", attrs={"class": "show-time"})
            if curr_res_open_time is None:
                continue
            curr_res_open_time = curr_res_open_time.text

            # 将字符串拆分为序列
            open_time_lis = str(curr_res_open_time).split("-")
            open_time_liss = open_time_lis[0]
            open_time_list = str(open_time_liss).split(" ")

            curr_open_time = None

            # 判断时间复杂度，取第一个序列数值
            if len(open_time_list) > 0:
                first_open_time = open_time_list[0]
                curr_open_time = first_open_time.replace(".", "/")
            else:
                curr_open_time = None

            # TODO 捕获异常
            try:  # 尝试运行
                number_price = int(curr_res_price)
            except ValueError:  # 可能会发生的报错
                number_price = 0

            res.append(Ticket(  # 注意数据类型
                name=str(curr_res_show_time_name) if len(str(curr_res_show_time_name)) else None,
                _type=str(curr_res_show_time_type) if len(str(curr_res_show_time_type)) else None,
                images_url=str(curr_res_image_url) if len(str(curr_res_image_url)) else None,
                status=str(curr_res_image_url) if len(str(curr_res_image_url)) else None,
                address=str(curr_res_address) if len(str(curr_res_address)) else None,
                open_time=str(curr_open_time) if len(str(curr_open_time)) else None,
                price=number_price
            ))

        return res

    # 获取时间去掉小数点
    def __get_current_time(self):
        return str(datetime.now()).split(".")[0]

    # 定义日志 format()是一个字符串的方法，用于将占位符替换为指定的值
    def __print_info(self, text: str):
        print(self.__inner_log_tpl.format("INFO", self.__get_current_time(), StringUtil.clean_str(text)))

    def __print_warn(self, text: str):
        print(self.__inner_log_tpl.format("WARN", self.__get_current_time(), StringUtil.clean_str(text)))

    def __print_debug(self, text: str):
        print(self.__inner_log_tpl.format("Debug", self.__get_current_time(), StringUtil.clean_str(text)))

    def __print_error(self, text: str):
        print(self.__inner_log_tpl.format("ERROR", self.__get_current_time(), StringUtil.clean_str(text)))

    def __print_fatal(self, text: str):
        self.__print_error(text)


if __name__ == '__main__':
    pass
